package wsi.fusion;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.rygh.core.html.table.HtmlTable;
import org.rygh.core.html.table.TableData;
import org.rygh.core.html.table.TableTuple;
import org.rygh.core.util.UMath;
import org.rygh.core.util.counter.SimpleCounter;
import org.rygh.core.xml.XmlPath;
import org.rygh.semantic.model.rdf.Entity;
import org.rygh.semantic.model.rdf.Fact;
import org.rygh.semantic.model.rdf.Predicate;

import wsi.dataguide.tree.WSXmlLeaf;
import wsi.function.Variable;
import wsi.match.SubtreeController;
import wsi.resolve.FactItem;

public class FunctionalityFuser extends Fuser {

	private final Map<Variable, Predicate> varEntityNamingPredMap = new HashMap<>();
	private final Map<Variable, WSXmlLeaf> varEntityNamingLeafMap = new HashMap<>();

	public FunctionalityFuser(SubtreeController subtreeController) {
		super(subtreeController);

	}

	private void addNamingInfo(Variable var, Predicate predicate, WSXmlLeaf leaf) {
		varEntityNamingPredMap.put(var, predicate);
		varEntityNamingLeafMap.put(var, leaf);
	}

	private void findNamingPredicate(Variable var,
			SimpleCounter<String> factItemCounter) {
		Entity var_ent = new Entity(var.getValue(), false);

		Predicate predicate = null, pred;
		WSXmlLeaf dg_leaf = null;

		double func, invf, mean, max = -1;
		int c, freq = -1;

		for (Object obj : factItemCounter.valueSet()) {
			FactItem item = (FactItem) obj;

			for (Fact fact : item.getFactGroup()) {
				if (!fact.getSubject().equals(var_ent)
						|| !fact.getObject().isLiteral())
					continue;

				pred = fact.getPredicate();

				if (pred.isTemp())
					continue;

				func = functionalityCtrl.getFunctionality(pred);
				invf = functionalityCtrl.getInverseFunctionality(pred);

				mean = UMath.getHarmonicMean(func, invf);

				if (max <= mean) {
					c = factItemCounter.get(item.getKey());
					// System.out.printf("find naming %d %d %s\n", freq, c, item
					// .getXmlLeaf().getXpath());

					// prefer higher percent.
					if (max == mean && freq > 0) {
						if (freq > c)
							continue;

						// prefer shorter xpath.
						// if (dg_leaf.getXpath().size() < item.getXmlLeaf()
						// .getXpath().size())
						// continue;
					}

					max = mean;

					freq = c;
					predicate = pred;
					dg_leaf = item.getXmlLeaf();
				}
			}
		}

		if (predicate != null) {// for input entity
			assert dg_leaf != null;
			addNamingInfo(var, predicate, dg_leaf);
		}
	}

	@Override
	protected Entity fuse(Entity varEntity, WSXmlLeaf subroot) {
		Entity entity;
		Variable varo;
		XmlPath xpath;
		String value;

		if (varEntity.isLiteral()) {
			varo = new Variable(varEntity);
			xpath = variableManager.getLeaf(varo).getXpath();
			value = getValue(xpath, subroot);

			if (value == null || value.isEmpty())
				return null;

			entity = new Entity(value, true);
		} else {
			value = getValue(varEntity, subroot);

			if (value == null || value.isEmpty())
				return null;

			entity = new Entity(value);
		}

		return entity;
	}

	/**
	 * for input entity and center entities where they are the same with input
	 * entity, we take directly the input. because when it's used for other
	 * functions, name consistency helps extract yago subgraphs.
	 * 
	 * data fusion happens here.
	 * 
	 * @param entity
	 * @param subroot
	 * @return
	 */
	private String getValue(Entity entity, WSXmlLeaf subroot) {
		String var = entity.getValue();
		String value;

		if (var.equals(VariableManager.E_INPUT.getValue())
				|| (var.equals(VariableManager.E_CENTER.getValue()) && isInputEqualsToCenter())) {
			// make sure it is consistent with yago entity.
			value = input.getEntity().getValue();

			Entity ent = new Entity(input.getEntity());

			if (!ent.hasPrefix())
				value = "y:" + value;

		} else {
			Variable variable = new Variable(entity);
			WSXmlLeaf namingLeaf = varEntityNamingLeafMap.get(variable);

			if (namingLeaf == null)
				return null;

			XmlPath xpath = namingLeaf.getXpath();
			value = getValue(xpath, subroot);

			if (value == null)
				return null;

			value = "y:" + value;
		}

		return value;
	}

	public Map<Variable, WSXmlLeaf> getVarEntityNamingLeafMap() {
		return varEntityNamingLeafMap;
	}

	/**
	 * among all relations linking literals, pick the one with the highest
	 * harmonic mean of functionality and inverse functionality, use the string
	 * value there as the name for the entity..
	 */
	public void prepare(SimpleCounter<String> factItemCounter) {
		findNamingPredicate(VariableManager.E_INPUT, factItemCounter);
		findNamingPredicate(VariableManager.E_CENTER, factItemCounter);

		Set<Variable> vars = variableManager.getVarEntityInverseMap().keySet();

		for (Variable var : vars)
			findNamingPredicate(var, factItemCounter);
	}

	@Override
	public HtmlTable toHtmlTable() {
		TableData data = new TableData();
		TableTuple tuple = new TableTuple();
		tuple.add("Entity", "Location Predicate", "Naming Predicate",
				"Naming Xpath");
		data.add(tuple);

		Map<Variable, Predicate> varEntityInverseMap = variableManager
				.getVarEntityInverseMap();

		for (Variable var : varEntityNamingPredMap.keySet()) {

			tuple = new TableTuple();
			tuple.add(var.getValue());

			if (varEntityInverseMap.containsKey(var))
				tuple.add(varEntityInverseMap.get(var).toString());
			else
				tuple.add("NULL");

			if (varEntityNamingPredMap.containsKey(var)) {
				tuple.add(varEntityNamingPredMap.get(var).toString());
				tuple.add(varEntityNamingLeafMap.get(var).toString());
			} else
				tuple.add("NULL", "NULL");

			data.add(tuple);
		}

		data.sort(0);

		return new HtmlTable("Entity Variables", data, false);
	}
}
